/*
 * Copyright (c) 2007 Team and Concepts Limited.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Team and Concepts Limited nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY TEAM AND CONCEPTS LIMITED ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL TEAM AND CONCEPTS LIMITED BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.editgrid.api.client;

import java.io.IOException;
import java.text.ParseException;
import java.util.*;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.util.URIUtil;
import org.xml.sax.SAXException;

/**
 * Represents a spreadsheet resource on EditGrid.
 */
public class Spreadsheet extends EditGridEntity {
	private EditGridService service;
	protected Spreadsheet(EditGridService service, AtomEntry spreadsheetEntry) {
		super(spreadsheetEntry);
		this.service = service;
	}
	
	/**
	 * Returns the name of the spreadsheet represented by this object.
	 * @return The name of the spreadsheet represented by this object.
	 */
	public String getSpreadsheetName() {
		return this.getValue("spreadsheet-name");
	}
	
	/**
	 * Returns the name of the organisation owning the spreadsheet
	 * represented by this object.
	 * @return The name of the organisation owning the spreadsheet
	 * represented by this object.
	 */
	public String getOwnerOrganisation() {
		return this.getValue("owner.organisation");
	}
	
	/**
	 * Returns the name of the workspace owning the spreadsheet
	 * represented by this object.
	 * @return The name of the workspace owning the spreadsheet 
	 * represented by this object.
	 */
	public String getOwnerWorkspace() {
		return this.getValue("owner.workspace");
	}
	
	/**
	 * Returns the public readability of the spreadsheet represented by this object.
	 * @return True if the spreadsheet represented by this object is public readable
	 * or protected read.
	 */
	public boolean getPublicReadable() {
		return this.getBooleanValue("public-access.readable");
	}
	
	/**
	 * Returns the public writablility of the spreadsheet represented by this object.
	 * @return True if the spreadsheet represented by this object is public writable
	 * or protected write.
	 */
	public boolean getPublicWritable() {
		return this.getBooleanValue("public-access.writable");
	}
	
	/**
	 * Returns the protected readability of the spreadsheet represented by this object.
	 * @return True if the spreadsheet represented by this object is readable by users
	 * with a valid password.
	 */
	public boolean getProtectedRead() {
		return this.getBooleanValue("public-access.protected-read");
	}
	
	/**
	 * Returns the protected writability of the spreadsheet represented by this object.
	 * @return True if the spreadsheet represented by this object is writable by users
	 * with a valid password
	 */
	public boolean getProtectedWrite() {
		return this.getBooleanValue("public-access.protected-write");
	}
	
	/**
	 * Returns the readability of the authenticated user of this API session.
	 * @return True if the authenticated user of this API session is allowed to read
	 * the spreadsheet represented by this object.
	 */
	public boolean getUserReadable() {
		return this.getBooleanValue("user-access.readable");
	}
	
	/**
	 * Returns the authenticated user write access right of the spreadsheet.
	 * @return True if the authenticated user of this API session is allowed to write
	 * the spreadsheet represented by this object
	 */
	public boolean getUserWritable() {
		return this.getBooleanValue("user-access.writable");
	}
	
	/**
	 * Returns copyright license of the spreadsheet represented by this object.
	 * @return The copyright license of the spreadsheet represented by this object.
	 */
	public String getLicense() {
		return this.getValue("license");
	}
	
	/**
	 * Returns the note of the spreadsheet represented by this object.
	 * @return The note of the spreadsheet represented by this object.	
	 */
	public String getNote() {
		return this.getValue("note");
	}
	
	/**
	 * Returns the timezone associated to the spreadsheet represented by this object.
	 * @return The timezone associated to the spreadsheet represented by this object.
	 */
	public String getTimezone() {
		return this.getValue("timezone");
	}
	
	/**
	 * Returns the last modified time of the spreadsheet represented by this object.
	 * @return A <code>Date</code> object representing the last
	 * modified time of the spreadsheet represented by this object.
	 * @throws ParseException
	 */
	public Date getLastModifiedTime() throws ParseException {
		return this.getDateTimeValue("last-modified.time");
	}
	
	/**
	 * Retrieve the collection of sheets in the spreadsheet represented by this object.
	 * @return A list of <code>Sheet</code> in the spreadsheet represented by this object.
	 * @throws HttpException If a protocol exception occurs.
	 * @throws IOException If an I/O (transport) error occurs.
	 * @throws SAXException
	 * @throws EditGridServiceException If EditGrid API 2.0 service returns an error.
	 */
	public List<Sheet> getSheets() throws HttpException, IOException, SAXException, EditGridServiceException {
        AtomFeed feed = this.service.query("sheets/" + this.getResourcePath());
        List<Sheet> sheetList = new ArrayList<Sheet>();
        
        for (Iterator<AtomEntry> i = feed.getEntries().iterator(); i.hasNext();) {
        	AtomEntry entry = i.next();
        	sheetList.add(new Sheet(entry));
        }
        return sheetList;
	}
	
	/**
	 * Create a new sheet in the spreadsheet represented by this object.
	 * @param sheetName The name of the sheet to be created.
	 * @param sheetPosition The zero-based position index of the sheet to be created.
	 * @throws HttpException If a protocol exception occurs.
	 * @throws IOException If an I/O (transport) error occurs.
	 * @throws SAXException
	 * @throws EditGridServiceException If EditGrid API 2.0 service returns an error.
	 */
	public void createSheet(String sheetName, int sheetPosition)
			throws HttpException, IOException, SAXException, EditGridServiceException {
		AtomEntry entry = new AtomEntry();
		entry.setValue("sheet-name", sheetName);
		entry.setValue("sheet-number", Integer.toString(sheetPosition));
		this.service.create("sheets/" + this.getResourcePath(), entry);
	}
	
	/**
	 * Delete a sheet in the spreadsheet represented by this object.
	 * @param sheetName The name of the sheet to be deleted.
	 * @throws URIException
	 * @throws HttpException If a protocol exception occurs.
	 * @throws IOException If an I/O (transport) error occurs.
	 * @throws SAXException
	 * @throws EditGridServiceException If EditGrid API 2.0 service returns an error.
	 */
	public void deleteSheet(String sheetName)
			throws URIException, HttpException, IOException, SAXException, EditGridServiceException {
		this.service.delete("sheets/" + this.getResourcePath() + "/" + URIUtil.encodePath(sheetName));
	}
	
	/**
	 * RRename a sheet in the spreadsheet represented by this object.
	 * @param oldName The original name of the sheet.
	 * @param newName The new name of the sheet.
	 * @throws URIException
	 * @throws HttpException If a protocol exception occurs.
	 * @throws IOException If a protocol exception occurs.
	 * @throws SAXException
	 * @throws EditGridServiceException If EditGrid API 2.0 service returns an error.
	 */
	public void renameSheet(String oldName, String newName)
			throws URIException, HttpException, IOException, SAXException, EditGridServiceException {
        AtomEntry entry = this.service.queryEntry("sheets/" + this.getResourcePath() + "/" + URIUtil.encodePath(oldName));
        entry.setValue("sheet-name", newName);
        this.service.update("sheets/" + this.getResourcePath() + "/" + URIUtil.encodePath(oldName), entry);
	}
	
	/**
	 * Update a cell in a sheet of the spreadsheet represented by this object.
	 * @param sheetName The name of the sheet to be updated
	 * @param cellPosition The position of the cell to be updated.
	 * @param inputValue The new value of the cell.
	 * @throws HttpException If a protocol exception occurs.
	 * @throws IOException If a protocol exception occurs.
	 * @throws SAXException
	 * @throws EditGridServiceException If EditGrid API 2.0 service returns an error.
	 */
	public void setCellValue(String sheetName, CellPosition cellPosition, String inputValue)
			throws HttpException, IOException, SAXException, EditGridServiceException {
		AtomEntry entry = new AtomEntry();
		entry.setValue("input", inputValue);
		this.service.update("cells/" + this.getResourcePath() + "/" + URIUtil.encodePath(sheetName) + "/" + cellPosition, entry);
	}
	
	/**
	 *  Get the value of a cell in a sheet of the spreadsheet represented by this object.
	 * @param sheetName The name of the sheet to be retrieved.
	 * @param cellPosition The position of the cell to be retrieved.
	 * @return A <code>Cell</code> object contains the value of the cell retrieved.
	 * @throws HttpException If a protocol exception occurs.
	 * @throws IOException If a protocol exception occurs.
	 * @throws SAXException
	 * @throws EditGridServiceException If EditGrid API 2.0 service returns an error.
	 */
	public Cell getCellValue(String sheetName, CellPosition cellPosition)
			throws HttpException, IOException, SAXException, EditGridServiceException {
        AtomEntry entry = this.service.queryEntry("cells/" + this.getResourcePath() + "/" +
        		URIUtil.encodePath(sheetName) + "/" + cellPosition.toString());
        return new Cell(entry);
	}
	
	/**
	 * Get the value of a range of cells in a sheet of the spreadsheet represented by this object.
	 * @param sheetName The name of the sheet to be retrieved.
	 * @param range The range of cells to be retrieved.
	 * @return A list of <code>Cell</code> objects contain the value of cells retrieved.
	 * @throws HttpException If a protocol exception occurs.
	 * @throws IOException If a protocol exception occurs.
	 * @throws SAXException
	 * @throws EditGridServiceException If EditGrid API 2.0 service returns an error.
	 */
	public List<Cell> getCellValueInRange(String sheetName, CellRange range)
			throws HttpException, IOException, SAXException, EditGridServiceException {
		QueryOption option = new QueryOption();
		option.setRange(range);
		AtomFeed feed = this.service.query("cells/" + this.getResourcePath() + "/" + URIUtil.encodePath(sheetName), option);
		List<Cell> cellList = new ArrayList<Cell>();
		for (Iterator<AtomEntry> i = feed.getEntries().iterator(); i.hasNext();) {
			AtomEntry entry = i.next();
			cellList.add(new Cell(entry));
		}
		return cellList;
	}
	
	/**
	 * Returns a list of shared workspaces of the spreadsheet represented by this object.
	 * @return A list of <code>ShareInfo</code> objects.
	 * @throws HttpException If a protocol exception occurs.
	 * @throws IOException If a protocol exception occurs.
	 * @throws SAXException
	 * @throws EditGridServiceException If EditGrid API 2.0 service returns an error.
	 */
	public List<ShareInfo> getSharedWorkspaces()
			throws HttpException, IOException, SAXException, EditGridServiceException {
		AtomFeed feed = this.service.query("shares/" + this.getResourcePath());
		List<ShareInfo> shareInfoList = new ArrayList<ShareInfo>();
		for (Iterator<AtomEntry> i = feed.getEntries().iterator(); i.hasNext();) {
			AtomEntry entry = i.next();
			shareInfoList.add(new ShareInfo(entry));
		}
		return shareInfoList;
	}
	
	/**
	 * Returns the <code>ShareInfo</code> of a shared workspace of the
	 * spreadsheet represented by this object.
	 * @param workspace The shared workspace to be query.
	 * @return A <code>ShareInfo</code> object.
	 * @throws HttpException If a protocol exception occurs.
	 * @throws IOException If a protocol exception occurs.
	 * @throws SAXException
	 * @throws EditGridServiceException If EditGrid API 2.0 service returns an error.
	 */
	public ShareInfo getShareInfo(Workspace workspace)
			throws HttpException, IOException, SAXException, EditGridServiceException {
		AtomEntry shareInfoEntry = this.service.queryEntry("shares/" + this.getResourcePath() + "/" + workspace);
		return new ShareInfo(shareInfoEntry);
	}
	
	/**
	 * Add a shared workspace to the spreadsheet represented by this object.
	 * @param workspace The workspace to be added.
	 * @param shareWritable Writability of the workspace to be added.
	 * @throws HttpException If a protocol exception occurs.
	 * @throws IOException If a protocol exception occurs.
	 * @throws SAXException
	 * @throws EditGridServiceException If EditGrid API 2.0 service returns an error.
	 */
	public void addSharedWorkspace(Workspace workspace, boolean shareWritable)
			throws HttpException, IOException, SAXException, EditGridServiceException {
		AtomEntry entry = new AtomEntry();
		entry.setValue("organisation", workspace.getOrganisation());
		entry.setValue("workspace", workspace.getWorkspace());
		entry.setValue("readable", "1");
		entry.setValue("writable", shareWritable ? "1" : "0");
		this.service.create("shares/" + this.getResourcePath(), entry);
	}
	
	/**
	 * Change the access right of a shared workspace to read-only.
	 * @param workspace The shared workspace to become read-only.
	 * @throws HttpException If a protocol exception occurs.
	 * @throws IOException If a protocol exception occurs.
	 * @throws SAXException
	 * @throws EditGridServiceException If EditGrid API 2.0 service returns an error.
	 */
	public void setSharedWorkspaceReadOnly(Workspace workspace)
			throws HttpException, IOException, SAXException, EditGridServiceException {
        AtomEntry entry = new AtomEntry();
        entry.setValue("organisation", workspace.getOrganisation());
        entry.setValue("workspace", workspace.getWorkspace());
        entry.setValue("readable", "1");
        entry.setValue("writable", "0");

        this.service.update("shares/" + this.getResourcePath() + "/" + workspace, entry);
	}
	
	/**
	 * Change the access right of a shared workspace to read-write.
	 * @param workspace The shared workspace to become read-write.
	 * @throws HttpException If a protocol exception occurs.
	 * @throws IOException If a protocol exception occurs.
	 * @throws SAXException
	 * @throws EditGridServiceException If EditGrid API 2.0 service returns an error.
	 */
	public void setSharedWorkspaceReadWrite(Workspace workspace)
			throws HttpException, IOException, SAXException, EditGridServiceException {
        AtomEntry entry = new AtomEntry();
        entry.setValue("organisation", workspace.getOrganisation());
        entry.setValue("workspace", workspace.getWorkspace());
        entry.setValue("readable", "1");
        entry.setValue("writable", "1");

        this.service.update("shares/" + this.getResourcePath() + "/" + workspace, entry);
	}

	/**
	 * Remove a shared workspace from the spreadsheet represented by this object.
	 * @param workspace The shared workspace to be removed.
	 * @throws HttpException  If a protocol exception occurs.
	 * @throws IOException If a protocol exception occurs.
	 * @throws SAXException
	 * @throws EditGridServiceException If EditGrid API 2.0 service returns an error.
	 */
    public void deleteSharedWorkspace(Workspace workspace)
    		throws HttpException, IOException, SAXException, EditGridServiceException
    {
        this.service.delete("shares/" + this.getResourcePath() + "/" + workspace);
    }
}
